package com.lemon.blog.web.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.lemon.blog.web.domain.*;
import com.lemon.blog.web.mapper.*;
import com.lemon.blog.web.service.IBlBlogService;
import com.lemon.blog.web.vo.KeyWordVO;
import com.lemon.common.core.constant.Constants;
import com.lemon.common.core.exception.ServiceException;
import com.lemon.common.redis.service.RedisService;
import com.lemon.common.security.service.BaseServiceImpl;
import com.lemon.common.security.utils.OptEntityUtils;
import com.lemon.common.security.utils.SecurityUtils;
import com.lemon.system.api.domain.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 包名称：com.lemon.blog.service.impl
 * 类名称：BlBlogServiceImpl
 * 类描述：博客管理Service业务层处理
 * 创建人：@author huangpeng
 * 创建时间：2022年04月25日 17:12
 */
@Service
public class BlBlogServiceImpl extends BaseServiceImpl<BlBlog> implements IBlBlogService {

    @Autowired
    private BlBlogMapper blBlogMapper;

    @Autowired
    private BlTagMapper blTagMapper;

    @Autowired
    private BlBlogTagMapper blBlogTagMapper;

    @Autowired
    private BlTypeMapper blTypeMapper;

    @Autowired
    private BiUserMapper biUserMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private RedisService redisService;

    @Override
    public List<BlBlog> selectBlBlogList(BlBlog blBlog) {
        Long currUserId = null;
        if (StrUtil.isNotEmpty(SecurityUtils.getUsername())) {
            currUserId = SecurityUtils.getLoginUser().getUserid();
        }
        blBlog.setCurrUserId(currUserId);
        List<BlBlog> blogList = blBlogMapper.selectBlBlogList(blBlog);
        for (BlBlog blog : blogList) {
            if (blog.getAdminId() != null) {
                SysUser sysUser = sysUserMapper.selectByAdminId(blog.getAdminId());
                BiUser biUser = new BiUser();
                biUser.setId(sysUser.getUserId());
                biUser.setUserCode(sysUser.getUserName());
                biUser.setUserName(sysUser.getNickName());
                biUser.setAvatar(sysUser.getAvatar());
                biUser.setGender(Long.parseLong(sysUser.getSex()));
                biUser.setNickName(sysUser.getNickName());
                biUser.setUserType(Constants.STR_ZERO);
                blog.setUser(biUser);
            } else {
                blog.setUser(biUserMapper.selectByPrimaryKey(blog.getUserId()));
            }
        }
        return blogList;
    }

    @Override
    public List<BlBlog> queryRecommendList() {
        return blBlogMapper.queryRecommendList();
    }

    @Override
    public int read(BlBlog blBlog) {
        Long readCount = blBlogMapper.selectByPrimaryKey(blBlog.getId()).getBlogRead();
        BlBlog model = new BlBlog(blBlog.getId());
        model.setBlogRead(readCount == null ? 0L : readCount + 1);
        OptEntityUtils.preUpdate(model);
        return blBlogMapper.updateByPrimaryKeySelective(model);
    }

    @Override
    public List<Map<String, Object>> queryTimeList() {
        return blBlogMapper.queryTimeList();
    }

    @Override
    public void saveSearchKeyWord(Map<String, Object> map) {
        if (map.containsKey("keyWord")) {
            String keyWord = map.get("keyWord").toString().toLowerCase();
            String redisKey = Constants.KEY_WORD_KEY + keyWord;
            if (redisService.hasKey(redisKey) && redisService.getExpire(redisKey, TimeUnit.MINUTES) > 0) {
                String strKeyWordVO = redisService.getCacheObject(redisKey);
                KeyWordVO keyWordVO = JSONObject.parseObject(strKeyWordVO, KeyWordVO.class);
                keyWordVO.setSearchTime(keyWordVO.getSearchTime() + 1);
                Long expire = redisService.getExpire(redisKey, TimeUnit.MINUTES);
                redisService.setCacheObject(redisKey, JSONObject.toJSONString(keyWordVO), expire, TimeUnit.MINUTES);
            } else {
                KeyWordVO keyWordVO = new KeyWordVO();
                keyWordVO.setKeyWord(keyWord);
                keyWordVO.setSearchTime(1);
                redisService.setCacheObject(redisKey, JSONObject.toJSONString(keyWordVO), Constants.KEYWORD_EXPIRATION_DAY_MINUTES, TimeUnit.MINUTES);
            }
        }
    }

    @Override
    public List<KeyWordVO> queryKeyWordList() {
        String prefix = Constants.KEY_WORD_KEY.concat("*");
        Collection<String> keys = redisService.keys(prefix);
        List<KeyWordVO> keyWordVOList = new ArrayList<>();
        if (!keys.isEmpty()) {
            Collection<String> strDataList = redisService.getMultiCacheMapValue(new ArrayList<>(keys));
            for (String strData : strDataList) {
                KeyWordVO keyWordVO = JSONObject.parseObject(strData, KeyWordVO.class);
                keyWordVOList.add(keyWordVO);
            }
            if (!keyWordVOList.isEmpty()) {
                Collections.sort(keyWordVOList);
                // 截取前10个
                if (keyWordVOList.size() > 10) {
                    keyWordVOList = keyWordVOList.subList(0, 10);
                }
            }
        }
        return keyWordVOList;
    }

    @Override
    public boolean submitApproval(BlBlog blBlog) {
        BlBlog existBlog = blBlogMapper.selectByPrimaryKey(blBlog.getId());
        if (existBlog.getApprovalStatus().equals(Constants.STR_THREE)) {
            BlBlog modal = new BlBlog();
            modal.setId(blBlog.getId());
            modal.setApprovalStatus(Constants.STR_TWO);
            OptEntityUtils.preUpdate(modal);
            blBlogMapper.updateByPrimaryKeySelective(modal);
        } else if (existBlog.getApprovalStatus().equals(Constants.STR_TWO)) {
            throw new ServiceException("当前文章已经提交了复核，无需再次提交");
        } else{
            throw new ServiceException("当前文章已经复核完成");
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int insert(BlBlog blBlog) {
        blBlog.setUserId(SecurityUtils.getLoginUser().getUserid());
        blBlog.setAdminId(null);
        blBlog.setApprovalStatus(Constants.STR_THREE);
        OptEntityUtils.preInsert(blBlog);
        int count = blBlogMapper.insertSelective(blBlog);
        insertBlogTagRel(blBlog);
        increaseTypeBlogCount(blBlog.getBlogType());
        return count;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(BlBlog blBlog) {
        // 权限校验，只能更新自己的文章
        blBlog.setApprovalStatus(Constants.STR_THREE);
        checkAuth(blBlog);
        OptEntityUtils.preUpdate(blBlog);
        clearBlogTagRel(blBlog.getId());
        insertBlogTagRel(blBlog);
        decreaseTypeBlogCount(blBlog.getId());
        increaseTypeBlogCount(blBlog.getBlogType());
        return blBlogMapper.updateByPrimaryKeySelective(blBlog);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByBatch(Long[] ids) {
        for (Long blogId : ids) {
            // 只能删除自己的文章
            checkAuth(new BlBlog(blogId));
            clearBlogTagRel(blogId);
        }
        return blBlogMapper.deleteByBatch(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteByPrimaryKey(Long id) {
        // 只能删除自己的文章
        checkAuth(new BlBlog(id));
        clearBlogTagRel(id);
        return blBlogMapper.deleteByPrimaryKey(id);
    }

    @Override
    public BlBlog selectByPrimaryKey(Long id) {
        Long currUserId = null;
        if (StrUtil.isNotEmpty(SecurityUtils.getUsername())) {
            currUserId = SecurityUtils.getLoginUser().getUserid();
        }
        BlBlog blog = blBlogMapper.selectByPrimaryKeyAndCurrUserId(id, currUserId);
        if (blog.getAdminId() != null) {
            SysUser sysUser = sysUserMapper.selectByAdminId(blog.getAdminId());
            BiUser biUser = new BiUser();
            biUser.setId(sysUser.getUserId());
            biUser.setUserCode(sysUser.getUserName());
            biUser.setUserName(sysUser.getNickName());
            biUser.setAvatar(sysUser.getAvatar());
            biUser.setGender(Long.parseLong(sysUser.getSex()));
            biUser.setNickName(sysUser.getNickName());
            biUser.setUserType(Constants.STR_ZERO);
            blog.setUser(biUser);
        } else {
            blog.setUser(biUserMapper.selectByPrimaryKey(blog.getUserId()));
        }
        return blog;
    }

    /**
     * 插入博客标签关联信息
     * @Title: insertBlogTagRel
     * @Description: 插入博客标签关联信息
     * @param blBlog 博客数据
     * @throws:
     * @author: huangpeng
     * @Date: 2022/5/1 18:09
     */
    private void insertBlogTagRel(BlBlog blBlog) {
        List<BlTag> tagList = blBlog.getTagList();
        for (BlTag nTag : tagList) {
            BlTag blTag = blTagMapper.selectByPrimaryKey(nTag.getId());
            blTag.setTagBlogCount(blTag.getTagBlogCount() + 1);
            OptEntityUtils.preUpdate(blTag);
            blTagMapper.updateByPrimaryKeySelective(blTag);

            BlBlogTag blogTag = new BlBlogTag();
            blogTag.setBlogId(blBlog.getId().toString());
            blogTag.setTagId(nTag.getId());
            OptEntityUtils.preInsert(blogTag);
            blBlogTagMapper.insert(blogTag);
        }
    }

    /**
     * 清除原有标签信息
     * @Title: clearBlogTagRel
     * @Description: 清除原有标签信息
     * @param blogId 博客id
     * @return: void
     * @throws:
     * @author: huangpeng
     * @Date: 2022/5/1 18:29
     */
    private void clearBlogTagRel(Long blogId) {
        // 清除原有标签信息
        List<BlBlogTag> blogTagList = blBlogTagMapper.selectByBlogId(blogId.toString());
        blogTagList = blogTagList.stream().collect(Collectors.collectingAndThen(
                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing((BlBlogTag::getTagId)))), ArrayList::new)
        );
        for (BlBlogTag blogTag : blogTagList) {
            BlTag blTag = blTagMapper.selectByPrimaryKey(blogTag.getTagId());
            if (blTag != null) {
                blTag.setTagBlogCount(blTag.getTagBlogCount() > 0 ? (blTag.getTagBlogCount() - 1) : 0);
                OptEntityUtils.preUpdate(blTag);
                blTagMapper.updateByPrimaryKeySelective(blTag);
            }
        }
        blBlogTagMapper.deleteByBlogId(blogId.toString());
    }

    /**
     * 分类的文章数量加1
     * @Title: increaseTypeBlogCount
     * @Description: 分类的文章数量加1
     * @param blogType 博客类型
     * @return: void
     * @throws:
     * @author: huangpeng
     * @Date: 2022/5/2 1:21
     */
    private void increaseTypeBlogCount(String blogType) {
        BlType blType = blTypeMapper.selectBlTypeByTypeCode(blogType);
        if (blType != null) {
            blType.setTypeBlogCount(blType.getTypeBlogCount() + 1);
            OptEntityUtils.preUpdate(blType);
            blTypeMapper.updateByPrimaryKeySelective(blType);
        }
    }

    /**
     * 分类文章数量减1
     * @Title: decreaseTypeBlogCount
     * @Description: 分类文章数量减1
     * @param blogId 博客id
     * @return: void
     * @throws:
     * @author: huangpeng
     * @Date: 2022/5/2 1:24
     */
    private void decreaseTypeBlogCount(Long blogId) {
        BlBlog blBlog = blBlogMapper.selectByPrimaryKey(blogId);
        BlType blType = blTypeMapper.selectBlTypeByTypeCode(blBlog.getBlogType());
        if (blType != null && blType.getTypeBlogCount() > 0) {
            blType.setTypeBlogCount(blType.getTypeBlogCount() - 1);
            OptEntityUtils.preUpdate(blType);
            blTypeMapper.updateByPrimaryKeySelective(blType);
        }
    }

    /**
     * 校验是否有编辑或者删除权限
     * @Title: checkAuth
     * @Description: 校验是否有编辑或者删除权限
     * @param blBlog
     * @return: void
     * @throws:
     * @author: huangpeng
     * @Date: 2023/4/30 20:45
     */
    private void checkAuth(BlBlog blBlog) {
        BlBlog existBlog = blBlogMapper.selectByPrimaryKey(blBlog.getId());
        if (!SecurityUtils.getLoginUser().getUserid().equals(existBlog.getUserId())) {
            throw new ServiceException("无权限操作当前数据！");
        } else {
            // 不允许更改作者
            blBlog.setAdminId(null);
            blBlog.setUserId(null);
        }
    }
}
